﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Windows.Forms;
using Archiver;
using CRC32;
using Language;
using LuaBoard;
using LuaInterface;
using Microsoft.Win32;
using System.Xml;

namespace LuaC
{
    #region Event
    public enum LuaClassEventType
    {
        Status,
        log,
        log_error,
        log_Warning,
        log_debug
    }
    public enum LuaClassProgressBarCmd
    {
        hide,
        init_show,
        value
    }
    public class LuaClassMsgEventArgs : EventArgs
    {
        private LuaClassEventType _EventType;
        private string _msg;

        public LuaClassMsgEventArgs(string messageData, LuaClassEventType eventType)
        {
            _EventType = eventType;
            _msg = messageData;
        }
        public LuaClassEventType EventType
        {
            get { return _EventType; }
            set { _EventType = value; }
        }
        public string Message
        {
            get { return string.Format("{0}",_msg); }
        }
    }
    public class LuaClassProgressBarEventArgs : EventArgs
    {
        private int _max = -1;
        private int _min = -1;
        private int _value = -1;
        public LuaClassProgressBarEventArgs(int value)
        {
            _value = value;
        }
        public LuaClassProgressBarEventArgs(int min,int max)
        {
            _max = max;
            _min = min;
        }
        public LuaClassProgressBarCmd cmd
        {
            get
            {
                if (_value != -1)
                {
                    return LuaClassProgressBarCmd.value;
                }
                if (_max != -1 && _max > _min && _max != 0)
                {
                    return LuaClassProgressBarCmd.init_show;
                }
                return LuaClassProgressBarCmd.hide;
            }
        }
        public int Max
        {
            get { return _max; }
            set { _max = value; }
        }
        public int Min
        {
            get { return _min; }
            set { _min = value; }
        }
        public int Value
        {
            get { return _value; }
            set { _value = value; }
        }
    }
    #endregion

    public class LuaClass
    {
        private string _name;
        private Lua luaVM;
        private LanguageData lang = Language.CommLanguageData.data;
        public event EventHandler<LuaClassMsgEventArgs> LuaMsgEvent;
        public event EventHandler<LuaClassProgressBarEventArgs> LuaProgressBarEvent;

        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
        public Lua LuaVM
        {
            get { return luaVM; }
            set { luaVM = value; }
        }

        #region lua function
        public void print(string s)
        {
            SendMsg(s, LuaClassEventType.log);
        }
        public void PutLog(string s) //print
        {

            SendMsg(s, LuaClassEventType.log);
        }
        public void PutError(string s)
        {
            SendMsg(s, LuaClassEventType.log_error);
        }
        public void PutErrorEx(Exception e)
        {
            string msg = e.Source;
            while (true)
            {
                msg += Environment.NewLine + e.Message;
                if (e.InnerException != null)
                {
                    e = e.InnerException;
                }
                else break;
            }
            SendMsg(msg, LuaClassEventType.log_error);
        }
        public void PutWarning(string s)
        {
            SendMsg(s, LuaClassEventType.log_Warning);
        }
        public void PutStatus(string s)
        {
            SendMsg(s, LuaClassEventType.Status);
        }
        public void PutDebug(string s)
        {
            SendMsg(s, LuaClassEventType.log_debug);
        }
        public void ProgressBar_init(int min, int max)
        {
            EventHandler<LuaClassProgressBarEventArgs> temp = this.LuaProgressBarEvent;
            if (temp != null)
                temp(this, new LuaClassProgressBarEventArgs(min, max));
        }
        public void ProgressBar_value(int value)
        {
            EventHandler<LuaClassProgressBarEventArgs> temp = this.LuaProgressBarEvent;
            if (temp != null)
                temp(this, new LuaClassProgressBarEventArgs(value));
        }
        public void ProgressBar_hide()
        {
            EventHandler<LuaClassProgressBarEventArgs> temp = this.LuaProgressBarEvent;
            if (temp != null)
                temp(this, new LuaClassProgressBarEventArgs(-1, -1));
        }
        public void DoFileUnicode(string LuaFilePath)
        {
            if (!File.Exists(LuaFilePath))
            {
                PutError(string.Format("{0} : {1}" ,lang.GetString("File does not exist."), LuaFilePath));
                return;
            }
            string file = "";
            try
            {
                StreamReader rw = new StreamReader(LuaFilePath, Encoding.Unicode);
                file = rw.ReadToEnd();
                rw.Close();
                
            }
            catch (Exception ex)
            {
                PutError(string.Format("{0} : {1}",lang.GetString("Error reading file."), ex.Message));
            }
            string FileName = Path.GetFileName(LuaFilePath);
            try
            {
                this.luaVM.DoString(file, FileName);
            }
            catch (Exception ex)
            {
                PutErrorEx(ex);
            }

        }
        public int Unicode_len(string str)
        {
            return str.Length;
        }
        public string GetString(string str)
        {
            return lang.GetString(str);
        }
        public string GetRegistryKeyValue(string source,string location, string key)
        {
            RegistryKey LevelKey = null;
            switch(source.ToLower())
            {
                case "localmachine":
                    LevelKey = Registry.LocalMachine;
                    break;
                case "currentuser":
                    LevelKey = Registry.CurrentUser;
                    break;
                case "users":
                    LevelKey = Registry.Users;
                    break;
                case "classesroot":
                    LevelKey = Registry.ClassesRoot;
                    break;
                case "currentconfig":
                    LevelKey = Registry.CurrentConfig;
                    break;
            }
            if (LevelKey == null) return "";
            RegistryKey userKey = LevelKey.OpenSubKey(location, false);
            if (userKey == null) return ""; LevelKey.CreateSubKey(location,RegistryKeyPermissionCheck.ReadWriteSubTree);
            return userKey.GetValue(key).ToString();
        }
        public bool SetRegistryKeyValue(string source, string location, string key, object value)
        {
            RegistryKey LevelKey = null;
            switch (source.ToLower())
            {
                case "localmachine":
                    LevelKey = Registry.LocalMachine;
                    break;
                case "currentuser":
                    LevelKey = Registry.CurrentUser;
                    break;
                case "users":
                    LevelKey = Registry.Users;
                    break;
                case "classesroot":
                    LevelKey = Registry.ClassesRoot;
                    break;
                case "currentconfig":
                    LevelKey = Registry.CurrentConfig;
                    break;
            }
            if (LevelKey == null) return false;
            RegistryKey userKey = LevelKey.OpenSubKey(location, true);

            if (userKey == null) return false;
            userKey.SetValue(key, value);
            return true;
        }
 
        public void RunExe(string path, string Arguments,string WorkingDirectory)
        {
            Process run = new Process();
            // FileName 是要執行的檔案
            run.StartInfo.FileName = path;
            run.StartInfo.Arguments = Arguments;
            run.StartInfo.WorkingDirectory = WorkingDirectory;
            run.Start();

        }
        public void SaveTxtFile(string SavePath, string txt)
        {
            try
            {
                StreamWriter rw = new StreamWriter(SavePath,false, Encoding.Unicode);
                rw.Write(txt);
                rw.Close();
            }
            catch (Exception ex)
            {
                PutError(string.Format("{0} : ({1}) {2}", lang.GetString("Save file failed."), SavePath, ex.Message.ToString()));
            }
        }
        public string LoadTextFile(string FilePath)
        {
            if (!File.Exists(FilePath))
            {
                PutError(string.Format("{0} : {1}", lang.GetString("File does not exist."), FilePath));
                return "";
            }
            string text = "";
            try
            {
                StreamReader rw = new StreamReader(FilePath, Encoding.Unicode);
                text = rw.ReadToEnd();
                rw.Close();

            }
            catch (Exception ex)
            {
                PutError(string.Format("{0} : {1}", lang.GetString("Error reading file."), ex.Message.ToString()));
            }
            return text;
        }

        public LuaTable ArrayToTable(object[] array)
        {
            object[] o = this.luaVM.DoString("return {}");
            LuaTable table = (LuaTable)o[0];
            int i = 0;
            System.Collections.IEnumerator myEnumerator = array.GetEnumerator();
            while ((myEnumerator.MoveNext()) && (myEnumerator.Current != null))
            {
                table[i + 1] = myEnumerator.Current;
            }
            return table;
        }
        public string GetEncodingName()
        {
            return Encoding.Default.WebName;
        }
        public string GetCRC32(string file)
        {
            CRC_File cf = new CRC_File(file);
            return cf.CRC32CheckSumHex;
        }
        public int? CompareVersion(string file, int Major, int Minor, int Build,int Revision)
        {
            if (!File.Exists(file))
            {
                PutError(string.Format("{0} : {1}", lang.GetString("File does not exist."), file));
                return null;
            }
            FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(file);
            int[] p = new int[4];
            p[0] = fvi.ProductMajorPart - Major;
            p[1] = fvi.ProductMinorPart - Minor;
            p[2] = fvi.ProductBuildPart - Build;
            p[3] = fvi.ProductPrivatePart - Revision;
            for (int i = 0; i < 4; i++)
            {
                if (p[i] > 0) return 1;
                if (p[i] < 0) return -1;
            }
            return 0;
        }
        public string GetVersion(string file)
        {
            FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(file);
            return fvi.ProductVersion;
        }
        public INI_RW.IniFile iniFile(string ini_path)
        {
            INI_RW.IniFile ini = new INI_RW.IniFile(ini_path);
            return ini;
        }
        public System.Windows.Forms.AnchorStyles SetFormAnchor(bool Top, bool Bottom, bool Left, bool Right)
        {
            System.Windows.Forms.AnchorStyles Anchor = new AnchorStyles();
            if (Top) Anchor = Anchor | System.Windows.Forms.AnchorStyles.Top;
            if (Bottom) Anchor = Anchor | System.Windows.Forms.AnchorStyles.Bottom;
            if (Left) Anchor = Anchor | System.Windows.Forms.AnchorStyles.Left;
            if (Right) Anchor = Anchor | System.Windows.Forms.AnchorStyles.Right;
            return Anchor;
        }
        public LuaTable GetDirAllFilesPath(string dir, string seaarchPattern)
        {
            if (seaarchPattern == null) seaarchPattern = "*.*";
            DirectoryInfo di = new DirectoryInfo(dir);
            List<string> paths = new List<string>();
            object[] o = this.luaVM.DoString("return {}");
            LuaTable table = (LuaTable)o[0];
            int count = 1;
            GetDirAllFilesPath_sub(di, ref table, ref count, seaarchPattern);
            return table;

        }
        private void GetDirAllFilesPath_sub(DirectoryInfo dir,ref LuaTable table,ref int count,string seaarchPattern)
        {
            FileInfo[] files;
            files = dir.GetFiles(seaarchPattern);
            
            foreach (FileInfo fi in files)
            {
                table[count++] = fi.FullName;
            }

            DirectoryInfo[] dirs = dir.GetDirectories();
            foreach (DirectoryInfo di in dirs)
            {
                GetDirAllFilesPath_sub(di, ref table, ref count, seaarchPattern);
            }
        }
        public LuaTable GetDirAllFilesPath_DateSort(string dir, string seaarchPattern)
        {
            if (seaarchPattern == null) seaarchPattern = "*.*";

            FileInfo[] arrFileInfo = new DirectoryInfo(dir).GetFiles(seaarchPattern);
            Array.Sort(arrFileInfo, new FilesDateComparer());

            List<string> paths = new List<string>();
            object[] o = this.luaVM.DoString("return {}");
            LuaTable table = (LuaTable)o[0];

            for (int i = 0; i < arrFileInfo.Length; i++)
            {
                table[i + 1] = arrFileInfo[i];
            }
            return table;
        }
        //C# How to sort files by their creation date?
        private class FilesDateComparer : IComparer
        {
            public int Compare(object x, object y)
            {
                int iResult;
                FileInfo oFileX = (FileInfo)x;
                FileInfo oFileY = (FileInfo)y;

                if (oFileX.LastWriteTime == oFileY.LastWriteTime)
                {
                    iResult = 0;
                }
                else
                    if (oFileX.LastWriteTime > oFileY.LastWriteTime)
                    {
                        iResult = 1;
                    }
                    else
                    {
                        iResult = -1;
                    }
                return iResult;
            }
        }
        public void FileCopy(string Title,LuaTable fcd)
        {
            List<FileCopyData> fcds = new List<FileCopyData>();
            int i=1;
            while (true)
            {
                if (fcd[i] == null) break;
                fcds.Add((FileCopyData)fcd[i++]);
            }
            if (fcds.Count == 0) return;
            FileCopyForm fcf = new FileCopyForm(Title);
            fcf.fileCopyData = fcds;
            fcf.ShowDialog();
            fcf.Dispose();
        }
        public FileCopyData NewFileCopyData(string sourcePath, string distPath, bool overwite, bool move, string Ext)
        {
            FileCopyData fcd = new FileCopyData(sourcePath, distPath, overwite, move, Ext);
            return fcd;
        }

        public bool Compress7Z(string filePath, string outPath)
        {
            bool r = C7Z.Compress7Z(filePath, outPath);
            if (!r)
                PutError(string.Format("{0} : {1}", lang.GetString("Compressed file faile."), filePath));
            return r;
        }

        public bool ExtractingFile(string sourceArc, string Targetpath)
        {
            bool r = C7Z.ExtractingFile(sourceArc, Targetpath);
            if (!r)
                PutError(string.Format("{0} : {1}", lang.GetString("Extract file failed."), sourceArc));
            return r;
        }
        public LuaTable GetCompressFileNames(string sourceArc)
        {
            string[] list = C7Z.GetCompressFileNames(sourceArc);
            
            object[] o = this.luaVM.DoString("return {}");
            LuaTable table = (LuaTable)o[0];

            if (list == null) return table;

            for (int i = 0; i < list.Length; i++)
            {
                table[i + 1] = (string)list[i];
            }
            return table;
            
        }
        public XmlNode GetXmlAttributesItem(XmlNode Node,string Attributes, string Name)
        {
            foreach (XmlNode xn in Node)
            {
                if (xn.Attributes[Attributes].Value == Name) return xn.ChildNodes[0];
            }
            return null;
        }

        #endregion

        private void test()
        {
            //RegistryKey hkml = Registry.LocalMachine;
            //hkml.OpenSubKey(
        }
        public LuaClass(string name,string path)
        {
            _name = name;

            this.luaVM = new Lua();
            this.luaVM.HookException += new EventHandler<HookExceptionEventArgs>(this.LuaHookException);
            this.luaVM.DebugHook += new EventHandler<DebugHookEventArgs>(this.LuaDebugHookException);

            #region Register Global Variable
            this.luaVM.NewTable("setting");
            this.luaVM["NewLine"] = Environment.NewLine;
            this.luaVM["PathSelf"] = path;
            this.luaVM["PathLuaBoard"] = System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
            this.luaVM["Is64bit"] = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("PROCESSOR_ARCHITEW6432"));
            #endregion

            #region Register Function
            this.luaVM.RegisterFunction("print", this, this.GetType().GetMethod("print"));

            this.luaVM.NewTable("board");
            this.luaVM.RegisterFunction("board.PutLog", this, this.GetType().GetMethod("PutLog"));
            this.luaVM.RegisterFunction("board.PutError", this, this.GetType().GetMethod("PutError"));
            this.luaVM.RegisterFunction("board.PutWarning", this, this.GetType().GetMethod("PutWarning"));
            this.luaVM.RegisterFunction("board.PutStatus", this, this.GetType().GetMethod("PutStatus"));
            this.luaVM.RegisterFunction("board.PutDebug", this, this.GetType().GetMethod("PutDebug"));
            this.luaVM.RegisterFunction("board.ProgressBar_init", this, this.GetType().GetMethod("ProgressBar_init"));
            this.luaVM.RegisterFunction("board.ProgressBar_value", this, this.GetType().GetMethod("ProgressBar_value"));
            this.luaVM.RegisterFunction("board.ProgressBar_hide", this, this.GetType().GetMethod("ProgressBar_hide"));
            this.luaVM.NewTable("unicode");
            this.luaVM.RegisterFunction("unicode.dofile", this, this.GetType().GetMethod("DoFileUnicode"));
            this.luaVM.RegisterFunction("unicode.len", this, this.GetType().GetMethod("Unicode_len"));
            this.luaVM.RegisterFunction("unicode.SaveTxtFile", this, this.GetType().GetMethod("SaveTxtFile"));
            this.luaVM.RegisterFunction("unicode.LoadTextFile", this, this.GetType().GetMethod("LoadTextFile"));
            this.luaVM.RegisterFunction("string.getstring", this, this.GetType().GetMethod("GetString"));
            this.luaVM.NewTable("Registry");
            this.luaVM.RegisterFunction("Registry.GetRegistryKeyValue", this, this.GetType().GetMethod("GetRegistryKeyValue"));
            this.luaVM.RegisterFunction("Registry.SetRegistryKeyValue", this, this.GetType().GetMethod("SetRegistryKeyValue"));
            this.luaVM.NewTable("Process");
            this.luaVM.RegisterFunction("Process.run", this, this.GetType().GetMethod("RunExe"));
            this.luaVM.NewTable("FileIO");
            this.luaVM.RegisterFunction("FileIO.GetCRC32", this, this.GetType().GetMethod("GetCRC32"));
            this.luaVM.RegisterFunction("FileIO.GetDirAllFilesPath", this, this.GetType().GetMethod("GetDirAllFilesPath"));
            this.luaVM.RegisterFunction("FileIO.GetDirAllFilesPath_DateSort", this, this.GetType().GetMethod("GetDirAllFilesPath_DateSort"));
            this.luaVM.RegisterFunction("FileIO.FileCopy", this, this.GetType().GetMethod("FileCopy"));
            this.luaVM.RegisterFunction("FileIO.NewFileCopyData", this, this.GetType().GetMethod("NewFileCopyData"));
            this.luaVM.RegisterFunction("FileIO.Compress7Z", this, this.GetType().GetMethod("Compress7Z"));
            this.luaVM.RegisterFunction("FileIO.ExtractingFile", this, this.GetType().GetMethod("ExtractingFile"));
            this.luaVM.RegisterFunction("FileIO.GetCompressFileNames", this, this.GetType().GetMethod("GetCompressFileNames"));
            this.luaVM.RegisterFunction("FileIO.CompareVersion", this, this.GetType().GetMethod("CompareVersion"));
            this.luaVM.RegisterFunction("FileIO.GetVersion", this, this.GetType().GetMethod("GetVersion"));
            this.luaVM.RegisterFunction("FileIO.iniFile", this, this.GetType().GetMethod("iniFile"));
            this.luaVM.NewTable("XmlItem");
            this.luaVM.RegisterFunction("XmlItem.GetXmlAttributesItem", this, this.GetType().GetMethod("GetXmlAttributesItem"));

            this.luaVM.RegisterFunction("SetFormAnchor", this, this.GetType().GetMethod("SetFormAnchor"));
            this.luaVM.RegisterFunction("ArrayToTable", this, this.GetType().GetMethod("ArrayToTable"));
            this.luaVM.RegisterFunction("GetEncodingName", this, this.GetType().GetMethod("GetEncodingName"));
            #endregion
        }

        public void Close()
        {
            this.luaVM.Close();
        }

        public void LuaHookException(object sender, HookExceptionEventArgs e)
        {
            SendMsg(string.Format("LuaHookException : {0}{1}", e.Exception.Message, Environment.NewLine), LuaClassEventType.log_error);
        }
        public void LuaDebugHookException(object sender, DebugHookEventArgs e)
        {
            
            SendMsg(string.Format("{0} : {1}{2}", e.LuaDebug.shortsrc,e.LuaDebug.currentline, Environment.NewLine), LuaClassEventType.log_debug);
        }

        private void SendMsg(string s, LuaClassEventType eType)
        {
            EventHandler<LuaClassMsgEventArgs> temp = this.LuaMsgEvent;
            if (temp != null)
                temp(this, new LuaClassMsgEventArgs(s, eType));
        }
    }
}
